package com.share.device.emqx.handler.impl;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.share.common.core.constant.DeviceConstants;
import com.share.common.core.exception.ServiceException;
import com.share.common.rabbit.constant.MqConst;
import com.share.common.rabbit.service.RabbitService;
import com.share.device.domain.Cabinet;
import com.share.device.domain.CabinetSlot;
import com.share.device.domain.PowerBank;
import com.share.device.domain.Station;
import com.share.device.emqx.annotation.GuiguEmqx;
import com.share.device.emqx.constant.EmqxConstants;
import com.share.device.emqx.handler.MassageHandler;
import com.share.device.service.ICabinetService;
import com.share.device.service.ICabinetSlotService;
import com.share.device.service.IPowerBankService;
import com.share.device.service.IStationService;
import com.share.order.api.domain.EndOrderVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@GuiguEmqx(topic = EmqxConstants.TOPIC_POWERBANK_CONNECTED)
@RequiredArgsConstructor
public class PowerBankConnectedHandler implements MassageHandler {

    private final ICabinetService cabinetService;

    private final IPowerBankService powerBankService;

    private final ICabinetSlotService cabinetSlotService;

    private final IStationService stationService;

    private final RedisTemplate redisTemplate;

    private final RabbitService rabbitService;

    /**
     * 处理充电宝连接事件
     * 当充电宝插入机柜插槽时，MQTT会发布此主题消息
     * 此方法负责更新相关设备状态并触发订单结束流程
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handleMessage(JSONObject message) {
        log.info("收到充电宝连接消息: {}", message.toJSONString());

        try {
            // 1. 消息幂等性处理
            String messageNo = message.getString("mNo");
            if (StringUtils.isEmpty(messageNo)) {
                log.error("消息编号为空，拒绝处理");
                return;
            }

            // 使用Redis实现分布式锁，防止消息重复处理
            // 锁有效期1小时，避免因系统异常导致的锁残留
            String key = "powerBank:connected:" + messageNo;
            Boolean isExist = redisTemplate.opsForValue().setIfAbsent(key, messageNo, 1, TimeUnit.HOURS);
            if (Boolean.FALSE.equals(isExist)) {
                log.info("重复消息，已处理: {}", messageNo);
                return;
            }

            // 2. 解析消息参数
            String cabinetNo = message.getString("cNo");    // 柜机编号
            String powerBankNo = message.getString("pNo");  // 充电宝编号
            String slotNo = message.getString("sNo");       // 插槽编号
            BigDecimal electricity = message.getBigDecimal("ety"); // 当前电量

            // 参数完整性校验
            if (StringUtils.isEmpty(cabinetNo) || StringUtils.isEmpty(powerBankNo) || StringUtils.isEmpty(slotNo) || electricity == null) {
                log.error("消息参数不完整: {}", message.toJSONString());
                throw new IllegalArgumentException("消息参数缺失");
            }

            // 3. 获取相关实体对象
            Cabinet cabinet = cabinetService.getBtCabinetNo(cabinetNo);
            if (cabinet == null) {
                log.error("未找到对应柜机: {}", cabinetNo);
                throw new ServiceException("柜机不存在");
            }

            PowerBank powerBank = powerBankService.getByPowerBankNo(powerBankNo);
            if (powerBank == null) {
                log.error("未找到对应充电宝: {}", powerBankNo);
                throw new ServiceException("充电宝不存在");
            }

            CabinetSlot cabinetSlot = cabinetSlotService.getBtSlotNo(cabinet.getId(), slotNo);
            if (cabinetSlot == null) {
                log.error("未找到对应插槽: 柜机ID={}, 插槽编号={}", cabinet.getId(), slotNo);
                throw new ServiceException("插槽不存在");
            }

            // 4. 更新充电宝状态
            // 充电宝状态: 0=未投放, 1=可用, 2=已租用, 3=充电中, 4=故障
            powerBank.setElectricity(electricity);
            if (electricity.compareTo(DeviceConstants.ELECTRICITY_MIN) > 0) {
                // 电量充足，可直接借用
                powerBank.setStatus("1");
                log.info("充电宝[{}]电量充足({}%), 设置状态为可用", powerBankNo, electricity.multiply(new BigDecimal("100")));
            } else {
                // 电量不足，需要充电
                powerBank.setStatus("3");
                log.info("充电宝[{}]电量不足({}%), 设置状态为充电中", powerBankNo, electricity.multiply(new BigDecimal("100")));
            }
            powerBankService.updateById(powerBank);

            // 5. 更新插槽状态
            cabinetSlot.setPowerBankId(powerBank.getId());
            cabinetSlot.setStatus("1"); // 已占用
            cabinetSlot.setUpdateTime(new Date());
            cabinetSlotService.updateById(cabinetSlot);
            log.info("插槽[{}]已占用，放置充电宝[{}]", slotNo, powerBankNo);

            // 6. 更新柜机统计信息
            int freeSlots = cabinet.getFreeSlots() - 1;     // 空闲插槽数减1
            int usedSlots = cabinet.getUsedSlots() + 1;     // 已用插槽数加1

            // 可用充电宝数量统计
            int availableNum = cabinet.getAvailableNum();
            if ("1".equals(powerBank.getStatus())) {
                // 新增一个可用充电宝
                availableNum++;
            }

            cabinet.setFreeSlots(freeSlots);
            cabinet.setUsedSlots(usedSlots);
            cabinet.setAvailableNum(availableNum);
            cabinet.setUpdateTime(new Date());
            cabinetService.updateById(cabinet);
            log.info("柜机[{}]状态更新: 空闲插槽={}, 已用插槽={}, 可用充电宝={}", cabinetNo, freeSlots, usedSlots, availableNum);

            // 7. 获取站点信息
            Station station = stationService.getByCabinetId(cabinet.getId());
            if (station == null) {
                log.error("未找到对应站点: 柜机ID={}", cabinet.getId());
                throw new ServiceException("站点不存在");
            }

            // 8. 构建结束订单消息
            // 充电宝连接可能表示用户归还了充电宝，需要触发订单结束流程
            EndOrderVo endOrderVo = new EndOrderVo();
            endOrderVo.setMessageNo(messageNo);           // 消息编号
            endOrderVo.setEndTime(new Date());            // 结束时间
            endOrderVo.setEndCabinetNo(cabinetNo);        // 归还柜机
            endOrderVo.setEndStationId(station.getId());  // 归还站点ID
            endOrderVo.setEndStationName(station.getName()); // 归还站点名称
            endOrderVo.setPowerBankNo(powerBankNo);       // 充电宝编号

            log.info("构建结束订单消息: {}", JSONObject.toJSONString(endOrderVo));

            // 9. 发送消息到订单系统
            rabbitService.sendMessage(MqConst.EXCHANGE_ORDER, MqConst.ROUTING_END_ORDER, JSONObject.toJSONString(endOrderVo));
            log.info("已发送结束订单消息，消息编号: {}", messageNo);

        } catch (Exception e) {
            log.error("处理充电宝连接消息失败: {}", e.getMessage(), e);
            // 抛出业务异常，触发事务回滚
            throw new ServiceException("处理充电宝连接消息失败");
        }
    }
}
